nv.defineSingleton("nv.util.Number", function ($this) {
    $this.format = function (stringOrNumber, decimalPlaces, prefix, postfix) {
        if (typeof decimalPlaces == "undefined") {
            decimalPlaces = 2;
        }
        if (typeof prefix == "undefined") {
            prefix = '';
        }
        if (stringOrNumber === '' || stringOrNumber === null) {
            return '';
        }
        var p = Number(stringOrNumber);
        if (isNaN(p)) {
            return '';
        }
        if (p < 0 && p.toFixed(decimalPlaces) == 0) {
            p = 0;
        }
        if (prefix && p < 0) {
            prefix = "-" + prefix;
            p = Math.abs(p);
        }
        var text = p.toFixed(decimalPlaces);
        if (prefix) {
            text = prefix + text;
        }
        if (postfix) {
            text = text + postfix;
        }
        return text;
    };
    $this.toOrdinal = function (n) {
        if (n == null) return "";
        n = Number(n);
        if (isNaN(n)) return "";
        n = Math.round(n);
        if (n >= 10 && n <= 20) {
            return n + "th";
        }
        return n + ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'][n % 10];
    };
    $this.formatWithCommas = function (stringOrNumber, decimalPlaces, prefix, postfix) {
        return addCommas($this.format(stringOrNumber, decimalPlaces, prefix, postfix));
    };
    $this.formatWithCommasAndSignIfPositive = function (n, decimalPlaces) {
        var prefix = '';
        if (Number(n) > 0) {
            prefix = '+';
        }
        var formatted = nv.util.Number.formatWithCommas(n, decimalPlaces, prefix);
        return (formatted === '' ? '' : formatted);
    };
    $this.formatDollar = function (n, places) {
        if (isNaN(places)) {
            places = (n == Math.floor(n)) ? 0 : 2;
        }
        return $this.formatWithCommas(n, places, '$', undefined);
    };
    $this.formatPercent = function (n, places) {
        if (isNaN(places)) {
            n = Number(n);
            if (Number(n.toFixed(0)) == n) {
                places = 0;
            } else if (Number(n.toFixed(1)) == n) {
                places = 1;
            } else {
                places = 2;
            }
        }
        return $this.format(n, places, undefined, '%');
    };
    $this.formatPercentAndSignIfPositive = function (n, decimalPlaces) {
        if (typeof decimalPlaces == "undefined") {
            decimalPlaces = 2;
        }
        var prefix = '';
        if (Number(n) > 0) {
            prefix = '+';
        }
        var formatted = nv.util.Number.format(n, decimalPlaces, prefix);
        return (formatted === '' ? '' : formatted + "%");
    };
    $this.formatHuman = function (originalNum, unit, significantDigits, decimalPrecision, roundType) {
        var formattedNumber = function () {
            var original = '';
            var prefix = '';
            var number = '';
            var power = '';
            var suffix = '';
            this.toString = function () {
                return '' + this.prefix + this.number + this.power + this.suffix + '';
            };
        };
        var labels = {
            'K': 1000,
            'M': 1000,
            'B': 1000,
            'T': 1000
        };
        if (typeof unit == 'undefined') {
            unit = 'number';
        }
        if (typeof significantDigits == 'undefined') {
            significantDigits = null;
        }
        if (typeof decimalPrecision == 'undefined') {
            decimalPrecision = null;
        }
        if (typeof roundType == 'undefined') {
            roundType = 'round';
        }
        var returnObj = new formattedNumber();
        returnObj.original = originalNum;
        returnObj.prefix = "";
        returnObj.number = 0;
        returnObj.power = "";
        returnObj.suffix = "";
        if (typeof originalNum == 'undefined' || originalNum == 0 || originalNum == null) return returnObj;
        if (unit == '' || unit == 'currency') {
            returnObj.prefix = '';
        } else if (unit == '%' || unit == 'percentage') {
            returnObj.suffix = '%';
        } else if (unit == 'number') {} else {
            returnObj.suffix = unit;
        }
        var multiplier = 1;
        if (originalNum < 0) {
            multiplier = -1;
            if (roundType == 'ceil') {
                roundType = 'floor';
            } else if (roundType == 'floor') {
                roundType = 'ceil';
            }
        }
        var newNum = Math.abs(originalNum);
        for (var label in labels) {
            if (labels.hasOwnProperty(label)) {
                var scale = labels[label];
                if (newNum < scale) {
                    break;
                }
                newNum /= scale;
                returnObj.power = label;
            }
        }
        var log10 = function (num) {
            return Math.round(Math.log(num) / Math.log(10), 2);
        };
        var roundFunc = Math[roundType];
        if (significantDigits !== null) {
            var decimalOrder = Math.ceil(log10(newNum));
            var powerDiff = decimalOrder - significantDigits;
            newNum /= Math.pow(10, powerDiff);
            if (powerDiff >= 0) {
                newNum = roundFunc(newNum) * Math.pow(10, powerDiff);
            } else {
                while (powerDiff++ < 0) {
                    newNum = roundFunc(newNum) / 10;
                }
            }
        } else if (decimalPrecision !== null) {
            newNum *= Math.pow(10, decimalPrecision);
            newNum = roundFunc(newNum) / Math.pow(10, decimalPrecision);
        }
        returnObj.number = newNum * multiplier;
        return returnObj;
    };
    var addCommas = function (nStr) {
        if (!nStr || nStr.length < 4) {
            return nStr;
        }
        var x = nStr.split('.');
        var x1 = x[0];
        var rgx = /(\d+)(\d{3})/;
        while (rgx.test(x1)) {
            x1 = x1.replace(rgx, '$1' + ',' + '$2');
        }
        if (x.length > 1) {
            return x1 + '.' + x[1];
        }
        return x1;
    };
    var ONES = ["", " One", " Two", " Three", " Four", " Five", " Six", " Seven", " Eight", " Nine", " Ten", " Eleven", " Twelve", " Thirteen", " Fourteen", " Fifteen", " Sixteen", " Seventeen", " Eighteen", " Nineteen"];
    var TENS = ["", "", " Twenty", " Thirty", " Forty", " Fifty", " Sixty", " Seventy", " Eighty", " Ninety"];
    var TRIPLETS = ["", " Thousand", " Million", " Billion", " Trillion"];
    var toEngishHelper = function (num, t) {
        var r = Math.floor(num / 1000);
        var x = Math.floor((num / 100) % 10);
        var y = Math.floor(num % 100);
        var s = "";
        if (x > 0) {
            s += ONES[x] + " Hundred";
        }
        if (t == 0) {
            s += " and";
        }
        if (y < 20) {
            s += ONES[y];
        } else {
            s += TENS[Math.floor(y / 10)] + ONES[y % 10];
        }
        if (s != "") {
            s += TRIPLETS[t];
        }
        if (r > 0) {
            return toEngishHelper(r, t + 1) + s;
        }
        return s;
    };
    $this.toEnglish = function (num) {
        num = Number(num);
        if (isNaN(num)) return "";
        if (num == 0) {
            return "Zero";
        }
        return toEngishHelper(Math.round(num), 0).substring(1);
    };
});